/*
 * This file is part of Ember.
 *
 * Ember is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ember is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ember.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) Grant Tyra 2010
 * granttyra@gmail.com
*/

#include "../libember/ember_types.h"
#include "../libember/ember_call_stack.h"
#include "../libember/ember_object.h"
#include "../libember/ember_list_type.h"
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>

#ifdef WIN32
#ifdef EMBER_PROTON_EXPORTS
#define EMBER_PROTON_EXPORT __declspec(dllexport)
#else
#define EMBER_PROTON_EXPORT __declspec(dllimport)
#endif
#else
#define EMBER_PROTON_EXPORT
#endif

int window_open = 1;
EmberDictionary *images = NULL;

EmberDictionary *GetArgDict() {
	object_t *arglist = call_stack_retrieve("argdict");
	return object_get_attribute(arglist, "dict");
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplayIsOpen() {
	return object_create_integer(window_open);
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetBlending() {
	EmberDictionary *args = GetArgDict();
	int enabled = ember_integer_value(EmberDictionaryGet(args, "enabled"));

	if (enabled)
		glEnable(GL_BLEND);
	else
		glDisable(GL_BLEND);

	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetTextureMapping() {
	EmberDictionary *args = GetArgDict();
	int enabled = ember_integer_value(EmberDictionaryGet(args, "enabled"));

	if (enabled)
		glEnable(GL_TEXTURE_2D);
	else
		glDisable(GL_TEXTURE_2D);

	return ember_null();
}

typedef struct ProtonVector_s {
	float x;
	float y;
	float z;
} ProtonVector;

typedef struct ProtonTransform_s {
	ProtonVector translation;
	ProtonVector rotation;
	ProtonVector scale;
} ProtonTransform;

ProtonVector VectorFromObject(object_t *obj) {
	ProtonVector vect;
	vect.x = ember_decimal_value(object_get_attribute(obj, "x"));
	vect.y = ember_decimal_value(object_get_attribute(obj, "y"));
	vect.z = ember_decimal_value(object_get_attribute(obj, "z"));
	return vect;
}

ProtonTransform TransformFromObject(object_t *obj) {
	ProtonTransform trans;
	trans.translation = VectorFromObject(object_get_attribute(obj, "trans"));
	trans.rotation = VectorFromObject(object_get_attribute(obj, "rot"));
	trans.scale = VectorFromObject(object_get_attribute(obj, "scale"));
	return trans;
}

EMBER_EXPORT object_t *EmberProtonDisplayPushTransform() {
	EmberDictionary *args = GetArgDict();
	object_t *transobj = EmberDictionaryGet(args, "trans");
	ProtonTransform trans = TransformFromObject(transobj);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();

	glTranslatef(trans.translation.x, trans.translation.y, trans.translation.z);
	glRotatef(trans.rotation.x, 1, 0, 0);
	glRotatef(trans.rotation.y, 0, 1, 0);
	glRotatef(trans.rotation.z, 0, 0, 1);
	glScalef(trans.scale.x, trans.scale.y, trans.scale.z);

	return ember_null();
}

EMBER_EXPORT object_t *EmberProtonDisplayPopTransform() {
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	return ember_null();
}

typedef struct ProtonVertex_s {
	float x;
	float y;
	float z;
	float u;
	float v;
} ProtonVertex;

ProtonVertex VertexFromObject(object_t *obj) {
	ProtonVertex vertex;
	vertex.x = ember_decimal_value(object_get_attribute(obj, "x"));
	vertex.y = ember_decimal_value(object_get_attribute(obj, "y"));
	vertex.z = ember_decimal_value(object_get_attribute(obj, "z"));
	vertex.u = ember_decimal_value(object_get_attribute(obj, "u"));
	vertex.v = ember_decimal_value(object_get_attribute(obj, "v"));
	return vertex;
}

void DrawVertex(ProtonVertex vertex) {
	glTexCoord2f(vertex.u, vertex.v);
	glVertex3f(vertex.x, vertex.y, vertex.z);
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplayDraw() {
	EmberDictionary *args = GetArgDict();
	int type = ember_integer_value(EmberDictionaryGet(args, "type"));
	EmberList *verts = ember_list_value(EmberDictionaryGet(args, "verts"));
	object_t *vertobj = NULL;
	ProtonVertex vertex;
	int i = 0;

	glBegin(type);

	for (i = 0; i < verts->size; i++) {
		vertobj = EmberListGet(verts, i);
		vertex = VertexFromObject(vertobj); 
		DrawVertex(vertex);
	}

	glEnd();
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetProjection() {
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetOrtho() {
	EmberDictionary *args = GetArgDict();	
	float left = ember_decimal_value(EmberDictionaryGet(args, "left"));
	float right = ember_decimal_value(EmberDictionaryGet(args, "right"));
	float top = ember_decimal_value(EmberDictionaryGet(args, "top"));
	float bottom = ember_decimal_value(EmberDictionaryGet(args, "bottom"));
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(left, right, bottom, top, 0.0f, 1.0f);
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplayClear() {
	EmberDictionary *args = GetArgDict();	
	float r = ember_decimal_value(EmberDictionaryGet(args, "r"));
	float g = ember_decimal_value(EmberDictionaryGet(args, "g"));
	float b = ember_decimal_value(EmberDictionaryGet(args, "b"));
	glClearColor(r, g, b, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonInit() {
	images = EmberDictionaryCreate();
	return object_create_integer(SDL_Init(SDL_INIT_EVERYTHING));
}

EMBER_PROTON_EXPORT object_t *EmberProtonGetTicks() {
	return object_create_integer(SDL_GetTicks());
}

EMBER_PROTON_EXPORT object_t *EmberProtonShutdown() {
	SDL_Quit();
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplayPresent() {
	SDL_GL_SwapBuffers();
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonProcessEvents() {
	SDL_Event message;

	while (SDL_PollEvent(&message)) {
		if (message.type == SDL_QUIT) {
			window_open = 0;
		}
	}

	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetMode() {
	EmberDictionary *dict = GetArgDict();
	int width = ember_integer_value(EmberDictionaryGet(dict, "width"));
	int height = ember_integer_value(EmberDictionaryGet(dict, "height"));
	int bpp = ember_integer_value(EmberDictionaryGet(dict, "depth"));
	int fullscreen = ember_integer_value(EmberDictionaryGet(dict, "fullscreen"));
	int flags = SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0);
	SDL_SetVideoMode(width, height, bpp, flags);
	return ember_null();
}

EMBER_PROTON_EXPORT object_t *EmberProtonDisplaySetTitle() {
	EmberDictionary *dict = GetArgDict();
	char *title = ember_string_value(EmberDictionaryGet(dict, "title"));
	SDL_WM_SetCaption(title, title);
	return ember_null();																									
}

typedef struct GLTextureData_s {
	int width;
	int height;
	int textureId;
} GLTextureData;

GLTextureData *LoadGLTexture(const char *path) {
	SDL_Surface *surface = SDL_LoadBMP(path);
	GLuint textureId = -1;
	GLTextureData *textureData = malloc(sizeof(GLTextureData));

	glGenTextures(1, &textureId);
	glBindTexture(GL_TEXTURE_2D, textureId);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);

	textureData->width = surface->w;
	textureData->height = surface->h;
	textureData->textureId = textureId;

	SDL_FreeSurface(surface);

	EmberDictionaryPut(images, path, textureData);
	return textureData;
}

EMBER_PROTON_EXPORT object_t *EmberProtonContentGetImage() {
	EmberDictionary *dict = GetArgDict();
	char *path = ember_string_value(EmberDictionaryGet(dict, "path"));
	GLTextureData *texture = EmberDictionaryGet(images, path);
	object_t *object = object_create("ProtonImage");
	
	if (texture == NULL) {
		texture =  LoadGLTexture(path);
	}

	object_put_attribute(object, "image", texture);
	return object;
}

